package editor.modelo;

import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.List;

import javax.swing.JOptionPane;

import editor.modelo.Portal.Clase;
import editor.modelo.Portal.Tipo;

/**
 * Un Floor mantiene toda la información de los espacios que lo contienen, pero
 * a su vez es un espacio con sus propios portales a otros espacios ya que forma
 * parte del grafo de caminos, pero a un nivel de abstraccion mas alto (para
 * achicar los espacios de busqueda.
 * 
 * @author Alejandro
 * 
 */
public class EditorFloor extends Floor {

	public EditorFloor() {
		inicializar();
	}

	@Override
	public void inicializar() {
		this.espacios = new ArrayList<Space>();
		this.portales = new ArrayList<Portal>();
		this.landmarks = new ArrayList<LandMark>();
		miInfo = new EditorSpace();
	}

	public void insertPortal(EditorPortal p) {
		this.portales.add(p);
	}

	private void insertSpace(EditorSpace sp) {
		this.espacios.add(sp);
	}

	public EditorSpace addNewSpace(float cx, float cy) {
		EditorSpace nuevoEspacio = new EditorSpace(new Point2D.Float(cx, cy),
				true);
		// nuevoEspacio.setCentro(cx, cy);
		nuevoEspacio.nombrePadre = this.getCompleteFloorName();
		this.insertSpace(nuevoEspacio);
		return nuevoEspacio;
	}

	public EditorPortal addNewInternalPortal(EditorSpace source,
			Point2D edgeSource, EditorSpace dest, Point2D edgeDest) {
		EditorPortal p = new EditorPortal();
		p.setSource(source);
		p.setTarget(dest);
		p.setPtoSource(edgeSource);
		p.setPtoTarget(edgeDest);
		p.setTipo(Tipo.door);
		p.setClase(Clase.intra_floor);
		this.portales.add(p);
		return p;

	}

	public void addNewUnlinkedPortal(EditorSpace space, Point2D pto) {
		EditorPortal p = new EditorPortal();
		p.setSource(space);
		p.setTarget(null);
		p.setPtoSource(pto);
		p.setPtoTarget(null);
		p.setTipo(Tipo.door);
		p.setClase(Clase.intra_floor);
		p.setBidirectional(false);
		this.portales.add(p);
	}

	public EditorPortal addNewExternalPortal(EditorSpace source, Point2D pto1) {
		EditorPortal p = new EditorPortal();
		p.setSource(source);
		p.setTarget(null);
		p.setPtoSource(pto1);
		p.setPtoTarget(null);
		p.setTipo(Tipo.door);
		p.setClase(Clase.inter_floor);
		p.setBidirectional(false);
		this.portales.add(p);
		return p;

	}

	public void checkFormat() {
		for (Space s : espacios) {
			this.correctOrientation(((EditorSpace) s));
		}
		calculateHeights();
	}

	public void correctOrientation(EditorSpace s) {
		int i = 0;

		while (true) {
			Point2D p1 = s.getBorde().get(i);
			Point2D p2 = s.getBorde().get(i + 1);
			Point2D p3 = s.getNextPoint(s.getBorde().get(i + 1));
			float z = zComponent(p1, p2, p3);

			// System.out.println(z);
			if (Math.abs(z) > 1500) {
				if (z > 0) {
					System.out.println("invertir " + s.getName()); // + p1 + p2
																	// +p3);
					invertPoints(s);
				}
				break;
			}
			i++;
			if (i == s.getBorde().size() - 1)
				break; // error
		}
	}

	public void move(int offX, int offY) {
		for (Space s : this.espacios) {
			((EditorSpace) s).move(offX, offY);
		}
		for (LandMark lm : this.landmarks) {
			lm.centro.setLocation(lm.centro.getX() + offX, lm.centro.getY()
					+ offY);
		}

	}

	/*
	 * Calcula la altura de todos los spaces segun la altura de las escaleras.
	 * No acepta inconsistencias (el resultado en tal caso es impredecible)
	 */
	public void calculateHeights() {
		for (Space s : espacios) {
			s.setHeight(null); // reseteo
		}
		if (this.referencia == null) {
			JOptionPane.showMessageDialog(null,
					"Piso " + this.getCompleteFloorName() + " sin referencia");
			return;
		}
		this.referencia.setHeight(this.alturaAbsouta); //establezco la referencia
		// int min = 0;
		int ctrl = 100;
		int cant; //cantidad flotando sin asignar
		do {
			ctrl--; //para que no cicle infinitamente
			cant = 0;
			for (Portal p : portales) {
				if (p.getClase() != Clase.intra_floor || p.getTarget() == null)
					continue; // no determina altura
				Space src = p.getSource();
				Space tar = p.getTarget(); // no puede ser null porque es intra
											// floor.

				int diferencial;
				if (p.getTipo().equals(Tipo.dstair)) {
					diferencial = -p.getDifferential();
				} else if (p.getTipo().equals(Tipo.ustair)) {
					diferencial = p.getDifferential();
				} else
					diferencial = 0;

				if (src.getHeight() == null && tar.getHeight() == null) {
					cant++;
					// src.setHeight(0);//dif >0
					// min = Math.min(min, diferencial);
					// tar.setHeight(diferencial);
				} else if (src.getHeight() != null && tar.getHeight() == null) {
					tar.setHeight(src.getHeight() + diferencial);
					// min = Math.min(min, src.getHeight()+diferencial);
				} else if (src.getHeight() == null && tar.getHeight() != null) {
					src.setHeight(tar.getHeight() - diferencial);
					// min = Math.min(min, tar.getHeight() - diferencial);
				} else {
					if (src.getHeight() + diferencial != tar.getHeight())
						System.out.println("error en alturas de "
								+ src.getName() + " con " + tar.getName());
				}
			}
			if(ctrl ==0 ) {System.out.println("existen pisos flotantes"); break;}
		} while (cant != 0);

	}

	private void invertPoints(EditorSpace s) {
		List<Point2D> borde = s.getBorde();
		for (Portal p : this.getPortales()) { // invertir portales
			if (p.source == s)
				p.ptoSource = p.source.getNextPoint(p.ptoSource);
			if (p.target == s)
				p.ptoTarget = p.target.getNextPoint(p.ptoTarget);
		}
		int izq = 0;
		int der = borde.size() - 1;
		Point2D aux;
		while (izq < der) {
			aux = borde.get(der);
			borde.set(der, borde.get(izq));
			borde.set(izq, aux);
			izq++;
			der--;
		}

	}

	private float zComponent(Point2D p1, Point2D p2, Point2D p3) {
		// vectores p3 - p2 y p1- p2
		Point2D u = new Point2D.Double(p3.getX() - p2.getX(), p3.getY()
				- p2.getY());
		Point2D v = new Point2D.Double(p2.getX() - p1.getX(), p2.getY()
				- p1.getY());
		// ux*vy – uy*vx

		return (float) (u.getX() * v.getY() - u.getY() * v.getX());
	}

	public LandMark addNewLandMark(float xMouse, float yMouse, Space parent) {
		LandMark lm = new EditorLandMark();
		lm.setCentro(xMouse, yMouse);
		lm.setParent(parent);
		lm.setTipo(LandMark.Tipo.values()[0]);
		lm.setName("ponme un nombre");
		getLandmarks().add(lm);
		return lm;
	}

}
